package com.qf.efserver.track.service.impl;

import com.alibaba.fastjson.JSON;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.qf.common.core.entity.ActivityRecords;
import com.qf.common.core.entity.MotionTrack;
import com.qf.common.core.utils.TokenUtils;
import com.qf.common.mp.utils.RabbitMqUtils;
import com.qf.common.redis.utils.RedisUtils;
import com.qf.efserver.sport.param.SportParam;
import com.qf.efserver.sport.service.SportService;
import com.qf.efserver.track.mapper.MotionTrackMapper;
import com.qf.efserver.track.param.SaveTrackParam;
import com.qf.efserver.track.param.TrackLogParam;
import com.qf.efserver.track.service.MotionTrackService;
import com.qf.efserver.track.vo.TrackVO;
import com.qf.efserver.user.personaldata.service.UsersService;
import org.springframework.beans.BeanUtils;
import org.springframework.stereotype.Service;

import javax.annotation.Resource;
import java.util.Date;
import java.util.List;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.stream.Collectors;

/**
 * Author hehecheng
 * Date 2024/11/16
 * 祝我们都能遇见真诚的人
 * 见字如面
 */
@Service
public class MotionTrackServiceImpl implements MotionTrackService {

    @Resource
    private MotionTrackMapper motionTrackMapper;

    @Resource
    private SportService sportService;

    @Resource
    private ThreadPoolExecutor threadPoolExecutor;

    @Resource
    private UsersService usersService;

    @Resource
    private RabbitMqUtils rabbitMqUtils;


    /**
     * 记录运动轨迹
     *
     * @param saveTrackParam
     * @return
     */
    @Override
    public Integer saveTrack(SaveTrackParam saveTrackParam) {
        Integer uid = TokenUtils.getUidFromToken();
        Integer tag = saveTrackParam.getTag();
        Integer activityId = saveTrackParam.getActivityId();
        String key = null;
        // 运动开始
        if (tag == 0 && activityId == null || activityId == 0) {
            // 创建活动记录表
            SportParam sportParam = new SportParam();
            sportParam.setActivityType(saveTrackParam.getActivityType().toString());
            sportParam.setUserId(uid);
            activityId = sportService.addActivity(sportParam);
            key = String.format("activity:%s:uid:%s", activityId, uid);
            // 将数据存入Redis中
            TrackVO trackVO = new TrackVO();
            BeanUtils.copyProperties(saveTrackParam, trackVO);
            trackVO.setActivityId(activityId);
            trackVO.setCreateTime(new Date());
            RedisUtils.listPush(key, trackVO);
        }
        // 运动进行中
        if (tag == 1) {
            key = String.format("activity:%s:uid:%s", activityId, uid);
            // 存redis
            TrackVO trackVO = new TrackVO();
            BeanUtils.copyProperties(saveTrackParam, trackVO);
            trackVO.setCreateTime(new Date());
            RedisUtils.listPush(key, trackVO);
        }
        // 运动结束
        if (tag == 2) {
            // 存redis
            key = String.format("activity:%s:uid:%s", activityId, uid);
            TrackVO trackVO = new TrackVO();
            BeanUtils.copyProperties(saveTrackParam, trackVO);
            trackVO.setCreateTime(new Date());
            RedisUtils.listPush(key, trackVO);
            // 运动结束 对活动表添加结束时间
            ActivityRecords activityRecords = new ActivityRecords();
            activityRecords.setActivityId(activityId);
            activityRecords.setEndTime(trackVO.getCreateTime());
            // 在对应的活动表中添加结束时间
            sportService.updateActivityById(activityRecords);
            // 异步处理运动表中的运动时间、运动距离、消耗卡路里、定位记录
            Integer finalActivityId = activityId;
            threadPoolExecutor.execute(() -> motionAnalysis(uid, finalActivityId));
            // 队列异步将redis中的运动轨迹存到数据库中
            TrackLogParam trackLogParam = new TrackLogParam(activityId, uid);
            rabbitMqUtils.sendMsg("ex_order", "track.log", JSON.toJSONString(trackLogParam));
        }
        return activityId;
    }

    /**
     * 获取历史活动轨迹
     *
     * @param activityId
     * @return
     */
    @Override
    public List<TrackVO> getHistoricalTracks(Integer activityId) {
        if (activityId == null) {
            throw new RuntimeException("活动id为空");
        }
        Integer uid = TokenUtils.getUidFromToken();
        List<TrackVO> trackVOS;
        String key = String.format("activity:%s:uid:%s", activityId, uid);
        List<Object> objects = RedisUtils.listGet(key, 0, -1);
        if (objects != null) {
            trackVOS = objects.stream().map(item -> (TrackVO) item).collect(Collectors.toList());
        } else {
            // 查询数据库
            List<MotionTrack> motionTracks = motionTrackMapper.selectList(new LambdaQueryWrapper<MotionTrack>()
                    .eq(MotionTrack::getActivityId, activityId)
                    .orderByAsc(MotionTrack::getCreateTime));
            if (motionTracks == null) {
                throw new RuntimeException("该活动不存在运动轨迹记录");
            }
            trackVOS = motionTracks.stream().map(item -> {
                TrackVO trackVO = new TrackVO();
                BeanUtils.copyProperties(item, trackVO);
                return trackVO;
            }).collect(Collectors.toList());
        }
        return trackVOS;
    }

    @Override
    public void batchInsertion(List<TrackVO> list) {
        motionTrackMapper.insertBatchTrack(list);
    }

    /**
     * 运动分析，计算距离、消耗卡路里、步数
     *
     * @param uid
     * @param activityId
     */
    public void motionAnalysis(Integer uid, Integer activityId) {
        // 获取运动数据
        List<TrackVO> trackVOS;
        String key = String.format("activity:%s:uid:%s", activityId, uid);
        List<Object> objects = RedisUtils.listGet(key, 0, -1);
        if (objects != null) {
            trackVOS = objects.stream().map(item -> (TrackVO) item).collect(Collectors.toList());
        } else {
            // 查询数据库
            List<MotionTrack> motionTracks = motionTrackMapper.selectList(new LambdaQueryWrapper<MotionTrack>()
                    .eq(MotionTrack::getActivityId, activityId)
                    .orderByAsc(MotionTrack::getCreateTime));
            if (motionTracks == null) {
                throw new RuntimeException("该活动不存在运动轨迹记录");
            }
            trackVOS = motionTracks.stream().map(item -> {
                TrackVO trackVO = new TrackVO();
                BeanUtils.copyProperties(item, trackVO);
                return trackVO;
            }).collect(Collectors.toList());
        }
        // 计算平均速度
        double totalDistance = calculateDistanceOfMovement(trackVOS);  //距离 m
        Date startDate = trackVOS.get(0).getCreateTime(); // 运动开始时间
        Date endDate = trackVOS.get(trackVOS.size() - 1).getCreateTime(); // 运动结束时间
        double totalTime = (endDate.getTime() - startDate.getTime()) / 1000.0;  // 运动时间
        double average = (totalTime > 0) ? (totalDistance / totalTime) : 0; // m/s
        // 计算消耗的卡路里
        double calories = 0.0;
        double MET = 0.0;  //代谢当量
        double speedInKmH = average * 3.6; // 将速度从 m/s 转换为 km/h
        switch ((int) speedInKmH / 3) {
            case 0: // 0 - 3 km/h
                MET = 2.5;
                break;
            case 1: // 3 - 6 km/h
                MET = 4.3;
                break;
            case 2: // 6 - 9 km/h
                MET = 7.0;
                break;
            default: // 9 km/h 及以上
                MET = 12.0;
                break;
        }
        double weight = usersService.getUser(uid).getWeight(); // 用户体重
        calories = MET * weight * totalTime / 3600.0;
        // 计算步数
        double strideLength = 0.75; // 假设的步幅，单位为米
        int steps = (int) (totalDistance / strideLength); // 计算步数
        ActivityRecords activityRecords = new ActivityRecords();
        activityRecords.setActivityId(activityId);
        int i = (int) totalTime / 60;
        activityRecords.setDuration(i == 0 ? 1 : i);
        activityRecords.setDistance(totalDistance / 1000);
        activityRecords.setSteps(steps);
        activityRecords.setCaloriesBurned((int) calories);
        sportService.updateActivityById(activityRecords);
    }

    /**
     * 通过经纬度计算运动距离
     *
     * @param trackVOS
     * @return
     */
    public Double calculateDistanceOfMovement(List<TrackVO> trackVOS) {
        double totalDistance = 0.0;
        for (int i = 0; i < trackVOS.size() - 1; i++) {
            TrackVO t1 = trackVOS.get(i);
            TrackVO t2 = trackVOS.get(i + 1);
            double distance = haversine(t1.getLng(), t1.getLat(), t2.getLng(), t2.getLat());
            totalDistance += distance;
        }
        return totalDistance;
    }

    /**
     * 计算两个经纬度之间的距离
     *
     * @param lon1 第一个经度
     * @param lat1 第一个纬度
     * @param lon2 第二个经度
     * @param lat2 第二个纬度
     * @return
     */
    public double haversine(double lon1, double lat1, double lon2, double lat2) {
        // 地球半径，单位为米
        final double EARTH_RADIUS = 6371000;
        // 将角度转换为弧度
        double lat1Rad = Math.toRadians(lat1);
        double lat2Rad = Math.toRadians(lat2);
        double deltaLat = Math.toRadians(lat2 - lat1);
        double deltaLon = Math.toRadians(lon2 - lon1);
        // 计算哈弗辛公式
        double a = Math.sin(deltaLat / 2) * Math.sin(deltaLat / 2)
                + Math.cos(lat1Rad) * Math.cos(lat2Rad)
                * Math.sin(deltaLon / 2) * Math.sin(deltaLon / 2);
        double c = 2 * Math.atan2(Math.sqrt(a), Math.sqrt(1 - a));
        // 计算距离
        return EARTH_RADIUS * c; // 距离
    }


}
